package cn.itcast.hotel;

import cn.itcast.hotel.pojo.HotelDoc;
import com.alibaba.fastjson.JSON;
import com.mysql.cj.log.Log;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.core.TermVectorsResponse;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.internal.util.StringUtil;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
public class EsSearchDemo {
    RestHighLevelClient client;

    @BeforeEach
    void beforeAll() {
        this.client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://192.168.200.130:9200")
        ));
    }

    @AfterEach
    void afterAll() throws IOException {
        client.close();
    }

    @Test
    public void searchAll() throws IOException {
        // indices
        SearchRequest request = new SearchRequest("hotel");

        request.source().query(QueryBuilders.matchAllQuery());

        SearchResponse search = client.search(request, RequestOptions.DEFAULT);

        handleResponse(search);
    }


    public void handleResponse(SearchResponse response) {

        SearchHits searchHits = response.getHits();

        // 得到查询的总条数
        TotalHits totalHits = searchHits.getTotalHits();
        //System.out.println(totalHits);
        log.info("查询的总条数为: {}", totalHits);

        // 得到最高的相关性算分
        long maxScore = (long) searchHits.getMaxScore();
        log.info("相关性的最高分为: {}", maxScore);

        // 得到查询的内容
        SearchHit[] hits = searchHits.getHits();

        Arrays.stream(hits).forEach(arr -> {
            // 将查询到的内容转化为json格式
            String jsonString = arr.getSourceAsString();
            // 执行反序列化操作  转为实体对象
            HotelDoc hotelDoc = JSON.parseObject(jsonString, HotelDoc.class);

            log.info(hotelDoc + "");
        });
    }

    // match查询
    @Test
    public void matchTest() throws IOException {
        SearchRequest request = new SearchRequest("hotel");

        request.source().query(QueryBuilders.matchQuery("all", "如家"));
        SearchResponse search = client.search(request, RequestOptions.DEFAULT);

        handleResponse(search);

    }

    // 精确查询
    @Test
    public void termAndRange1() throws IOException {
        SearchRequest request = new SearchRequest("hotel");

        request.source().query(QueryBuilders.termsQuery("city", "上海"));

        SearchResponse search = client.search(request, RequestOptions.DEFAULT);

        handleResponse(search);
    }

    // 精确查询
    @Test
    public void termAndRange2() throws IOException {
        SearchRequest request = new SearchRequest("hotel");

        request.source().query(QueryBuilders.rangeQuery("price").gte(100).lte(200));

        SearchResponse search = client.search(request, RequestOptions.DEFAULT);

        handleResponse(search);
    }

    // 布尔查询
    @Test
    public void boolTest() throws IOException {

        SearchRequest request = new SearchRequest("hotel");

        request.source().query(
                QueryBuilders.boolQuery()
                        .must(QueryBuilders.termQuery("city", "上海"))
                        .filter(QueryBuilders.rangeQuery("price").lte(300))
        );

        SearchResponse search = client.search(request, RequestOptions.DEFAULT);

        handleResponse(search);
    }

    // 排序分页查询
    @Test
    public void fromAndSize() throws IOException {
        int page = 1;
        int pageSize = 3;
        int currentSize = (page - 1) * pageSize;

        SearchRequest request = new SearchRequest("hotel");

        request.source().query(QueryBuilders.matchAllQuery()).from(currentSize).size(pageSize);

        request.source().sort("price", SortOrder.DESC);

        SearchResponse search = client.search(request, RequestOptions.DEFAULT);

        handleResponse(search);
    }

    // 高亮查询
    @Test
    public void testHigeLight() throws IOException {
        SearchRequest request = new SearchRequest("hotel");

        request.source().query(QueryBuilders.matchQuery("all", "如家"));

        request.source().highlighter(
                new HighlightBuilder()
                        .field("name")
                        .requireFieldMatch(false)
                        .preTags("<span>")
                        .postTags("</span>"));

        SearchResponse search = client.search(request, RequestOptions.DEFAULT);

        handleResponse2(search);
    }

    public void handleResponse2(SearchResponse response) {
        SearchHits searchHits = response.getHits();

        // 得到查询的总条数
        TotalHits totalHits = searchHits.getTotalHits();
        //System.out.println(totalHits);
        log.info("查询的总条数为: {}", totalHits);

        // 得到最高的相关性算分
        long maxScore = (long) searchHits.getMaxScore();
        log.info("相关性的最高分为: {}", maxScore);

        // 得到查询的内容
        SearchHit[] hits = searchHits.getHits();

        Arrays.stream(hits).forEach(arr -> {
            // 将查询到的内容转化为json格式
            String jsonString = arr.getSourceAsString();

            // 执行反序列化操作  转为实体对象
            HotelDoc hotelDoc = JSON.parseObject(jsonString, HotelDoc.class);
            //获取高亮字段
            Map<String, HighlightField> highlightFields = arr.getHighlightFields();

            if (!CollectionUtils.isEmpty(highlightFields)) {

                // 获取 name字段的数据  返回的是数组格式的
                HighlightField name = highlightFields.get("name");

                // 拼接数组
                String highlightName = StringUtil.join(name);

                hotelDoc.setName(highlightName);
            }
            log.info(hotelDoc + "");
        });
    }

    @Test
    public void testBucket() throws IOException {
        // 创建请求对象
        SearchRequest request = new SearchRequest("hotel");
        // 不展示数据
        request.source().size(0);
        // 配置聚合
        request.source().aggregation(
                AggregationBuilders.terms("brandAgg").field("brand").size(20)
        );
        // 接收响应
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 解析响应
        Aggregations aggregations = response.getAggregations();
        // 请求采用term聚合  接收也要term接收
        Terms brandAgg = aggregations.get("brandAgg");
        // 获取分组内容
        List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();

        ArrayList<HotelDoc> hotelDocs = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            String keyAsString = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            log.info("按照品牌分组的信息为: {} ,一共有: {}家", keyAsString, docCount);
            System.out.println("===================");
        }
    }

    @Test
    public void testBucket2() throws IOException {
        // 创建请求对象
        SearchRequest request = new SearchRequest("hotel");
        // 不展示数据
        request.source().size(0);
        // 配置聚合
        request.source().aggregation(
                AggregationBuilders.terms("brandAgg").field("brand").size(20)
        );
        // 接收响应
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 解析响应
        Aggregations aggregations = response.getAggregations();
        // 请求采用term聚合  接收也要term接收
        Terms brandAgg = aggregations.get("brandAgg");
        // 获取分组内容
        List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();

        ArrayList<HotelDoc> hotelDocs = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            String keyAsString = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            log.info("按照品牌分组的信息为: {} ,一共有: {}家", keyAsString, docCount);
            System.out.println("===================");
        }
    }

    @Test
    public void testBuckets() throws IOException {

        SearchRequest request = new SearchRequest("hotel");

        request.source().size(0);
        request.source().aggregation(AggregationBuilders.terms("brandAgg").field("brand").size(20));

        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        Aggregations aggregations = response.getAggregations();

        Terms brandAgg = aggregations.get("brandAgg");
        List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();

        for (Terms.Bucket bucket : buckets) {
            String keyAsString = bucket.getKeyAsString();
            System.out.println("keyAsString = " + keyAsString);
        }
    }

    @Test
    public void testBucketsWith() {
        try {
            SearchRequest request = new SearchRequest("hotel");

            request.source().suggest(new SuggestBuilder().addSuggestion("mySuggest",
                    SuggestBuilders.completionSuggestion("suggestion")
                            .prefix("sh")
                            .skipDuplicates(true)
                            .size(10)
            ));

            SearchResponse response = client.search(request, RequestOptions.DEFAULT);

            Suggest suggest = response.getSuggest();
            CompletionSuggestion mySuggest = suggest.getSuggestion("mySuggest");
            List<CompletionSuggestion.Entry.Option> options = mySuggest.getOptions();

            for (CompletionSuggestion.Entry.Option option : options) {
                String text = option.getText().string();
                System.out.println("text = " + text);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

}
